{
 主窗口：显示欢迎提示信息
}
unit unit_main;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, ExtCtrls, StdCtrls,
  base64, LCLType, LCLIntf, IdGlobal,
  Buttons, ActnList;

type

  { TForm_main }

  TForm_main = class(TForm)
    Action_begin: TAction;
    Action_end: TAction;
    Action_regedit: TAction;
    ActionList1: TActionList;
    Bevel1: TBevel;
    Bevel2: TBevel;
    Edit_name: TEdit;
    Edit_dept: TEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label_rec_tip: TLabel;
    Label_url: TLabel;
    Label_tip: TLabel;
    Panel1: TPanel;
    Shape_capture: TShape;
    SpeedButton1: TSpeedButton;
    SpeedButton2: TSpeedButton;
    SpeedButton3: TSpeedButton;
    StaticText1: TStaticText;
    StaticText2: TStaticText;
    Timer1: TTimer;
    // 关闭时运行
    procedure Action_beginExecute(Sender: TObject);
    procedure Action_endExecute(Sender: TObject);
    procedure Action_regeditExecute(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure NoShowMyself;
    procedure Timer1Timer(Sender: TObject);
  private

    Fullscreen: Tbitmap;
    Fullscreen_sent_befor: Tbitmap; // 前一次发送的图像
    procedure CaptureScreen;
    procedure SentCapturePic(myjpg: Tjpegimage);
  public

    procedure ShowMyself;
    procedure ShowMyMsg;
  end;

const
  c_I = 5; // 发送数据包后延时一下，以便更好地接收。默认：5ms

var
  Form_main: TForm_main;

implementation

uses
  Unit_DM, Unit_docker, Unit_other, Unit_setup;

  {$R *.lfm}

procedure TForm_main.ShowMyself;
begin
  ShowMyMsg;
end;

procedure TForm_main.NoShowMyself;
begin
end;

procedure TForm_main.ShowMyMsg;
begin
  // 显示提示信息
  if pos('错误', DM.S_MESSAGE) > 0 then
  begin
    Label_tip.Font.Color := clRed;
  end
  else if pos('提示', DM.S_MESSAGE) > 0 then
  begin
    Label_tip.Font.Color := clBlue;
  end
  else
  begin
    Label_tip.Font.Color := clWindowText;
  end;
  Label_tip.Caption := DM.S_MESSAGE;
  Label_tip.Refresh;

  Form_docker.StatusBar1.Panels.Items[2].Text :=
    Format('%4d-%2d-%2d %2d:%2d', [DM.W_y, DM.W_m, DM.W_d, DM.W_h, DM.W_f]);
end;

procedure TForm_main.Timer1Timer(Sender: TObject);
begin
  Timer1.Enabled := False;

  Shape_capture.Brush.Color := clBlue;
  Shape_capture.Refresh;

  CaptureScreen;

  Shape_capture.Brush.Color := clWhite;
  Shape_capture.Refresh;

  Timer1.Enabled := True;
end;

procedure TForm_main.FormCreate(Sender: TObject);
begin
  // 初始化
  with DM do
  begin
    IniPropStorage1.IniSection := '注册';
    IniPropStorage1.Active := True;
    Edit_name.Text := IniPropStorage1.ReadString('用户', '');
    Edit_dept.Text := IniPropStorage1.ReadString('部门', '');
    IniPropStorage1.Active := False;

    IniPropStorage1.IniSection := '参数';
    IniPropStorage1.Active := True;
    DM.g_left := IniPropStorage1.ReadInteger('left', 0);
    DM.g_up := IniPropStorage1.ReadInteger('up', 0);
    DM.g_down := IniPropStorage1.ReadInteger('down', 0);
    DM.g_rigth := IniPropStorage1.ReadInteger('rigth', 0);
    IniPropStorage1.Active := False;
  end;

  Fullscreen := TBitmap.Create;
  Fullscreen_sent_befor := Tbitmap.Create; // 前一次发送的图像
end;

procedure TForm_main.FormDestroy(Sender: TObject);
begin
  Fullscreen.Free;
  Fullscreen_sent_befor.Free;
end;

procedure TForm_main.Action_beginExecute(Sender: TObject);
begin

  if not DM.Open_UDPClient() then exit;

  Action_begin.Enabled := False;
  Action_end.Enabled := True;
  Form_setup.Edit_ip.Enabled := False;
  Form_setup.SB_save_ip.Enabled := False;
  Form_setup.SB_save_ip.Visible := False;

  Timer1.Enabled := True; // 开启定时截屏

  Fullscreen_sent_befor.Assign(nil); // 清前一次图像，必须发送第一个
end;

procedure TForm_main.Action_endExecute(Sender: TObject);
begin
  DM.IdUDPClient_sent.Send('ResetSystem');
  // 告诉图像服务器，本机已经停止传输，服务器无需超时检测
  Sleep(300);
  // 不要告诉系统服务器，因为系统服务器还要从客户机IP查找图像服务器IP

  DM.IdUDPClient_sent.Active := False;

  Action_begin.Enabled := True;
  Action_end.Enabled := False;
  Timer1.Enabled := False;
  Form_setup.Edit_ip.Enabled := True;
  Form_setup.SB_save_ip.Enabled := True;
  Form_setup.SB_save_ip.Visible := True;
  Shape_capture.Brush.Color := clWhite;
end;

procedure TForm_main.Action_regeditExecute(Sender: TObject);
var
  S: string;
begin
  if Trim(Edit_name.Text) = '' then
  begin
    Form_docker.ShowMyHint(Edit_name);
    Exit;
  end;
  if Trim(Edit_dept.Text) = '' then
  begin
    Form_docker.ShowMyHint(Edit_dept);
    Exit;
  end;

  DM.S_user_name := Trim(Edit_name.Text);
  DM.S_user_dept := Trim(Edit_dept.Text);
  DM.IniPropStorage1.IniSection := '注册';
  DM.IniPropStorage1.Active := True;
  DM.IniPropStorage1.WriteString('用户', DM.S_user_name);
  DM.IniPropStorage1.WriteString('部门', DM.S_user_dept);
  DM.IniPropStorage1.Active := False;

  // 转换UTF8，再base64编码，服务器收到后，自动增加待办事项
  S := EncodeStringBase64(AnsiToUtf8(Trim(Edit_name.Text))) + ' ' +
    EncodeStringBase64(AnsiToUtf8(Trim(Edit_dept.Text)));
  DM.IdUDPClient_setup.Send('SETUPUSER' + S);
end;

procedure TForm_main.CaptureScreen;
var
  DC: HDC;
  FullscreenCanvas: TCanvas;
  Myjpg: Tjpegimage;
  dx, dy, dw, dh: integer;
  s1, s2, s3: string;
begin

  // 截取屏幕并显示
  FullscreenCanvas := TCanvas.Create;

  with DM do
  begin
    //g_left, g_up, g_down, g_rigth
    dx := -1 * g_left;
    dy := -1 * g_up;
    dw := Screen.Width + g_rigth;
    dh := Screen.Height + g_down;

    Fullscreen.Assign(nil); // 清除图像
    Fullscreen.Width := Screen.Width + g_left + g_rigth;
    Fullscreen.Height := Screen.Height + g_up + g_down;
  end;

  DC := GetDC(0); //取得屏幕的DC

  FullscreenCanvas.Handle := DC;

  Fullscreen.Canvas.CopyRect(
    Rect(0, 0, Fullscreen.Width, Fullscreen.Height),
    FullscreenCanvas,
    Rect(dx, dy, dw, dh));

  ReleaseDC(0, DC);

  // 增加水印
  with Fullscreen.Canvas do
  begin
    Font.Size := 18;
    Brush.Style := bsclear;
    s1 := DM.S_user_name + ' (' + DM.S_user_dept + ')';
    s2 := 'IP:' + DM.IdUDPClient_sent.Host;
    s3 := FormatDateTime('YYYY-MM-DD hh:nn:ss', now);
    Font.Color := clYellow;
    Brush.Color := clBlack;
    TextOut(10, 10, s1);
    TextOut(10, 60, s2);
    TextOut(10, 100, s3);
  end;

  // Form_other显示图像
  if Form_docker.Shape2.Visible then
    Form_other.Image1.picture.Bitmap := Fullscreen;

  // 零点前后一分钟不发送
  DM.IsToday; // 更新一下时间变量
  if ((DM.W_h = 23) and (DM.W_f = 59)) or ((DM.W_h = 00) and (DM.W_f = 00)) then
  begin
    // 5. 发送空图片，需要服务器回传握手信息
    DM.IdUDPClient_sent.Send('SentEmptyPic');
    Form_setup.AddLog('SentEmptyPic-00:00', True); // 记录一下

  end
  else if ((Form_setup.TabSheet_debug.TabVisible) or // 调试模式，持续发送图片
    (not DM.IsHomology(Fullscreen, Fullscreen_sent_befor, 1000))) then
    // 非调试模式，前后图像差别大于100像素，发送图片
  begin
    try
      Myjpg := Tjpegimage.Create;
      Myjpg.Assign(Fullscreen); // 将BMP图象转成JPG格式
      Myjpg.CompressionQuality := 20; //StrToIntDef(Form_setup.ComboBox1.Text, 20);
      // JPG文件压缩百分比设置,数字越大图像越清晰,但数据也越大
      SentCapturePic(Myjpg); // 发送图像到服务器 1 2 3 4 发送类型
    finally
      Myjpg.Free;
    end;

    Fullscreen_sent_befor.Assign(Fullscreen); // 保存到变量“前一次发送”

    Form_setup.AddLog('SentPic', True); // 记录一下

  end
  else
  begin
    // 5. 发送空图片，需要服务器回传握手信息
    DM.IdUDPClient_sent.Send('SentEmptyPic');
    Form_setup.AddLog('SentEmptyPic-pix100', True); // 记录一下

  end;

  FullscreenCanvas.Free;
end;

procedure TForm_main.SentCapturePic(myjpg: Tjpegimage);
var
  MyjpgStream: TMemoryStream;
  MySendStream: TMemorystream;
  LeftSize: integer;
  SendSize: integer;
  StreamSize: integer;
  BufSize: integer;
  I: integer;
  S, S_size, S_para: string;
  tss: TStringStream;
begin
  I := 0; // 帧数

  MyjpgStream := TMemoryStream.Create;
  myjpg.SaveToStream(MyjpgStream); // MyjpgStream.Position 移动到 Size

  StreamSize := MyjpgStream.Size;

  S_size := Format('%10d', [StreamSize]); // 10位，前补0
  Form_other.Label_note.Caption :=
    '图像大小：' + S_size + Format('  尺寸：%3d * %3d',
    [Screen.Width + DM.g_left + DM.g_rigth, Screen.Height + DM.g_up + DM.g_down]);

  // 1.Begin + Size
  DM.IdUDPClient_sent.Send('Begin' + S_size);
  Form_setup.AddLog('====Begin===='); // 加日志
  Form_setup.AddLog('NUM : ' + IntToStr(DM.I_REC_SERVER_NUM)); // 加日志
  Form_setup.AddLog('Size: ' + S_size); // 加日志
  Sleep(c_I); // 延时一下，否则接收容易丢包

  // 2.Param
  S_para := Format('%4d', [DM.g_left]) + '|' + Format('%4d', [DM.g_up]) +
    '|' + Format('%4d', [DM.g_down]) + '|' + Format('%4d', [DM.g_rigth]) +
    '|' + Format('%1d', [Screen.MonitorCount]);
  DM.S_param := S_para; // 保存到全局变量
  DM.IdUDPClient_sent.Send('Param' + S_para);
  Sleep(c_I); // 延时一下，否则接收容易丢包

  // 如果有多个屏幕，又没有调整xy轴，提醒服务器
  if (DM.g_left = 0) and (DM.g_up = 0) and (DM.g_down = 0) and
    (DM.g_rigth = 0) and (Screen.MonitorCount > 1) then
  begin
    if not DM.B_param_screen_tip then
    begin
      DM.B_param_screen_tip := True; // 只提醒一次

      // 转换UTF8，再base64编码，服务器收到后，自动增加待办事项
      S := EncodeStringBase64(AnsiToUtf8(Trim(Edit_name.Text))) +
        ' ' + EncodeStringBase64(AnsiToUtf8(Trim(Edit_dept.Text)));
      DM.IdUDPClient_setup.Send('TIPHasMultScreen' + S);
    end;
  end;

  BufSize := DM.IdUDPClient_sent.BufferSize - 2; // 8192 - 2 = 8190
  if (BufSize >= StreamSize) then
  begin
    SendSize := StreamSize;
    try
      MySendStream := TMemoryStream.Create;
      MyjpgStream.Position := 0;
      tss := TStringStream.Create(Format('%.2d', [I]));
      MySendStream.CopyFrom(tss, 2);
      MySendStream.CopyFrom(MyjpgStream, SendSize);
      //DM.IdUDPClient_sent.SendBuffer(MySendStream.Memory^, SendSize + 2); // delphi
      DM.IdUDPClient_sent.SendBuffer(ToBytes(MySendStream.Memory, SendSize + 2));
      // 不需要,不改变：MySendStream.Position CopyFrom后自动调整指针位置
    finally
      tss.Free;
      MySendStream.Free;
    end;
  end
  else
  begin
    LeftSize := 0;

    while LeftSize < (StreamSize - BufSize) do
    begin
      SendSize := BufSize;
      try
        MySendStream := TMemoryStream.Create;
        MyjpgStream.Position := LeftSize;
        tss := TStringStream.Create(Format('%.2d', [I]));
        MySendStream.CopyFrom(tss, 2);
        MySendStream.CopyFrom(MyjpgStream, SendSize);
        DM.IdUDPClient_sent.SendBuffer(ToBytes(MySendStream.Memory, SendSize + 2));
      finally
        tss.Free;
        MySendStream.Free;
      end;

      LeftSize := LeftSize + BufSize;
      I := I + 1;
      Sleep(c_I); // 延时一下，否则接收容易丢包

      // 3.Next
      DM.IdUDPClient_sent.Send('Next');
      Form_setup.AddLog('Next ' + IntToStr(I));   // 加日志
      Sleep(c_I); // 延时一下，否则接收容易丢包
    end;

    if (LeftSize >= (StreamSize - BufSize)) then
    begin
      SendSize := StreamSize - LeftSize;
      try
        MySendStream := TMemoryStream.Create;
        MyjpgStream.Position := LeftSize;
        tss := TStringStream.Create(Format('%.2d', [I]));
        MySendStream.CopyFrom(tss, 2);
        MySendStream.CopyFrom(MyjpgStream, SendSize);
        DM.IdUDPClient_sent.SendBuffer(ToBytes(MySendStream.Memory, SendSize + 2));
      finally
        tss.Free;
        MySendStream.Free;
      end;
    end;
  end;

  // 4.num                                     // 当天的第几个
  Sleep(c_I);
  DM.IdUDPClient_sent.Send('Num' + Format('%.5d', [DM.I_REC_SERVER_NUM]));

  // 5.End
  Sleep(c_I * 4); // 延时久一点，等服务器接收数据包
  DM.IdUDPClient_sent.Send('End');
  Form_setup.AddLog('----End----'); // 加日志

  DM.S_MESSAGE := '发送图像总数: ' + IntToStr(DM.I_REC_SERVER_NUM);
  // + ' 帧数: ' + IntToStr(I);
  ShowMyself;
end;

end.
